package com.yuntian.service.Impl;

import com.yuntian.dto.AccountReceivableDTO;
import com.yuntian.mapper.OrderMapper;
import com.yuntian.service.OrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.sql.Date;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.yuntian.dto.InvoiceCreationResult;
import com.yuntian.dto.InvoiceOrderDTO;
import com.yuntian.dto.InvoiceHeaderDTO;
import com.yuntian.dto.InvoiceDetailDTO;
import com.yuntian.dto.PaymentCustomerDTO;
import com.yuntian.dto.PaymentInvoiceDTO;
import com.yuntian.dto.PaymentResult;

@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final OrderMapper orderMapper;

    /**
     * 根据订单号查询客户应收信息
     * @param orderCode 订单号
     * @return 客户应收信息DTO
     */
    @Override
    public AccountReceivableDTO findAccountReceivableByOrderCode(String orderCode) {
        AccountReceivableDTO dto = orderMapper.getCustomerAndOrderById(orderCode);
        if (dto == null) {
            return null;
        }
        Long receivable = dto.getAccountReceivable();
        Long balance = dto.getAccountReceivableBalance();
        long receivableVal = receivable == null ? 0L : receivable;
        long balanceVal = balance == null ? 0L : balance;
        dto.setBalanceAfterRegistration(balanceVal + receivableVal);
        return dto;
    }
    /**
     * 注册客户应收
     * @param orderCode 订单号
     * @param deliveryDate 交货日期
     * @return 客户应收信息DTO
     */
    @Transactional
    @Override
    public AccountReceivableDTO registerAccountReceivable(String orderCode, LocalDate deliveryDate) {
        AccountReceivableDTO dto = orderMapper.getCustomerAndOrderById(orderCode);
        if (dto == null) {
            return null;
        }
        Long receivable = dto.getAccountReceivable();
        long receivableVal = receivable == null ? 0L : receivable;
        // 更新客户应收余额
        orderMapper.updateAccountReceivableBalance(dto.getCustomerCode(), receivableVal);
        // 更新交货日期
        orderMapper.updateDeliveryDate(orderCode, Date.valueOf(deliveryDate));
        // 获取更新后的余额
        Long updatedBalance = orderMapper.selectBalanceAfterRegistration(orderCode);
        dto.setBalanceAfterRegistration(updatedBalance);
        dto.setDeliveryDate(deliveryDate.toString());
        return dto;
    }
    /**
     * 查询需要开票的客户列表
     * @return 客户代码列表
     */
    // PR20201：开票对象顾客列表
    @Override
    public List<String> getCustomersNeedingInvoice() {
        return orderMapper.selectCustomersNeedingInvoice();
    }
    /**
     * 创建发票
     * @param customerCode 客户代码
     * @param requestDate 发票请求日期
     * @return 发票创建结果DTO
     */
    // PR20201：开票处理
    @Transactional
    @Override
    public InvoiceCreationResult createInvoice(String customerCode, java.time.LocalDate requestDate) {
        java.util.List<InvoiceOrderDTO> orders = orderMapper.selectInvoiceOrdersByCustomer(customerCode);
        if (orders == null || orders.isEmpty()) {
            throw new IllegalStateException("M202001E：請求書を作成する必要の顧客がありません。");
        }
        long total = 0L;
        for (InvoiceOrderDTO o : orders) {
            Long amt = o.getOrderAmount();
            total += (amt == null ? 0L : amt);
        }
        String invoiceNo = nextInvoiceNo();
        orderMapper.insertInvoice(invoiceNo,Date.valueOf(requestDate));
        int updated = orderMapper.assignInvoiceNoToCustomerOrders(customerCode, invoiceNo);
        InvoiceCreationResult res = new InvoiceCreationResult();
        res.setInvoiceNo(invoiceNo);
        res.setCustomerCode(customerCode);
        res.setRequestDate(requestDate);
        res.setOrderCount(updated);
        res.setTotalAmount(total);
        return res;
    }

    private String nextInvoiceNo() {
        String max = orderMapper.selectMaxInvoiceNo();
        int next = 0;
        try {
            next = (max == null || max.isEmpty()) ? 1 : Integer.parseInt(max) + 1;
        } catch (NumberFormatException e) {
            next = 1;
        }
        return String.format("%04d", next);
    }

    @Override
    public InvoiceHeaderDTO getInvoiceHeader(String invoiceNo) {
        return orderMapper.selectInvoiceHeader(invoiceNo);
    }

    @Override
    public List<InvoiceDetailDTO> getInvoiceDetails(String invoiceNo) {
        List<InvoiceOrderDTO> rows = orderMapper.selectInvoiceOrdersByInvoiceNo(invoiceNo);
        List<InvoiceDetailDTO> details = new ArrayList<>();
        for (InvoiceOrderDTO r : rows) {
            Long excl = r.getOrderAmount() == null ? 0L : r.getOrderAmount();
            long tax = Math.round(excl * 0.04); // 消费税（4%）
            long incl = excl + tax;
            InvoiceDetailDTO d = new InvoiceDetailDTO(r.getOrderCode(), r.getOrderDate(), r.getDeliveryDate(), excl, tax, incl);
            details.add(d);
        }
        return details;
    }

    // ===== PR20301 入金登録 =====

    @Override
    public List<String> getCustomersNeedingPayment() {
        return orderMapper.selectCustomersNeedingPayment();
    }

    /**
     * 获取客户支付摘要
     * @param customerCode 客户代码
     * @return 客户支付摘要DTO
     */
    @Override
    public PaymentCustomerDTO getPaymentCustomerSummary(String customerCode) {
        return orderMapper.selectCustomerSummaryByCode(customerCode);
    }
    /**
     * 获取客户未入金发票列表
     * @param customerCode 客户代码
     * @return 未入金发票DTO列表
     */
    @Override
    public List<PaymentInvoiceDTO> getUnpaidInvoices(String customerCode) {
        return orderMapper.selectPaymentInvoicesByCustomer(customerCode);
    }

    /**
     * 入金登録
     * @param customerCode 客户代码
     * @param paymentDate 入金日付
     * @param invoiceNos 入金明細（請求書番号リスト）
     * @return 入金結果DTO
     */
    @Transactional
    @Override
    public PaymentResult registerPayment(String customerCode, LocalDate paymentDate, List<String> invoiceNos) {
        if (invoiceNos == null || invoiceNos.isEmpty()) {
            throw new IllegalArgumentException("M203002E：入金明細に少なくとも一つレコードを選択してください。");
        }
        if (!isPaymentDateValid(paymentDate)) {
            throw new IllegalArgumentException("入金日付は前年同日から本日までの範囲で入力してください。");
        }
        List<PaymentInvoiceDTO> unpaid = orderMapper.selectPaymentInvoicesByCustomer(customerCode);
        Map<String, Long> amountMap = new HashMap<>();
        for (PaymentInvoiceDTO row : unpaid) {
            amountMap.put(row.getInvoiceNo(), row.getInvoiceAmount() == null ? 0L : row.getInvoiceAmount());
        }
        long total = 0L;
        int count = 0;
        for (String no : invoiceNos) {
            Long amt = amountMap.get(no);
            if (amt != null) {
                total += amt;
                count++;
            }
        }
        if (count == 0) {
            throw new IllegalStateException("対象となる未入金の請求書が存在しません。");
        }
        // 入金額が現在の売掛残高を超える場合はエラーにする（負残高防止）
        Long currentBalanceObj = orderMapper.selectBalanceByCustomerCode(customerCode);
        long currentBalance = currentBalanceObj == null ? 0L : currentBalanceObj;
        if (total > currentBalance) {
            throw new IllegalStateException("M203003E：入金額が売掛残高を超えています。");
        }
        String paymentCode = nextPaymentCode();
        orderMapper.insertPayment(paymentCode, Date.valueOf(paymentDate));
        for (String no : invoiceNos) {
            if (amountMap.containsKey(no)) {
                orderMapper.assignPaymentCodeToInvoice(no, paymentCode);
            }
        }
        orderMapper.subtractAccountReceivableBalance(customerCode, total);
        Long after = orderMapper.selectBalanceByCustomerCode(customerCode);
        PaymentResult res = new PaymentResult();
        res.setPaymentCode(paymentCode);
        res.setCustomerCode(customerCode);
        res.setPaymentDate(paymentDate);
        res.setInvoiceCount(count);
        res.setPaidAmount(total);
        res.setBalanceAfterRegistration(after == null ? 0L : after);
        return res;
    }

    /**
     * 入金コードを生成
     * @return 入金コード
     */
    private String nextPaymentCode() {
        String max = orderMapper.selectMaxPaymentCode();
        int next;
        try {
            next = (max == null || max.isEmpty()) ? 1 : Integer.parseInt(max) + 1;
        } catch (NumberFormatException e) {
            next = 1;
        }
        return String.format("%04d", next);
    }

    private boolean isPaymentDateValid(LocalDate date) {
        LocalDate today = LocalDate.now();
        LocalDate lastYearSameDay = today.minusYears(1);
        return (date != null && !date.isBefore(lastYearSameDay) && !date.isAfter(today));
    }
}